home *** CD-ROM | disk | FTP | other *** search
- /********************************************************************************
-
- Gnucleus - A node application for the Gnutella network
- Copyright (C) 2000 John Marshall
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- For support, questions, comments, etc...
- E-Mail:
- swabby@c0re.net
-
- Address:
- 21 Cadogan Way
- Nashua, NH, USA 03062
-
- ********************************************************************************/
-
- // GnuTransfer.cpp : implementation file
- //
- #include "stdafx.h"
- #include "Gnucleus.h"
- #include "GnucleusDoc.h"
-
- #include "ViewSearch.h"
- #include "ViewTransfer.h"
-
- #include "GnuHash.h"
- #include "GnuControl.h"
- #include "GnuTransfer.h"
-
- #include <process.h>
-
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
-
- IMPLEMENT_DYNCREATE(CGnuTransfer, CAsyncSocket)
-
- /////////////////////////////////////////////////////////////////////////////
- // CGnuTransfer
-
- CGnuTransfer::CGnuTransfer()
- {
- Initialize();
- IsFileOpen = 0;
- }
-
- CGnuTransfer::CGnuTransfer(CGnuControl *pComm)
- {
- Initialize();
- GnuComm = pComm;
- }
-
- CGnuTransfer::Initialize()
- {
- GnuComm = NULL;
- next = NULL;
-
- IsFileOpen = 0;
- IsTransfering = 0;
-
- BytesCompleted = 0;
- OldBytesCompleted = 0;
- OldRate = 0;
-
- m_dwBytesIn = m_dwBytesOut = m_dwTotalBytesIn = m_dwTotalBytesOut
- = m_dwByteAllottmentOut = m_dwByteAllottmentIn = 0;
-
-
- AllocBw = -1;
-
- m_threadHandle = NULL;
- m_timeLastHeardAT = CTime::GetCurrentTime();
- }
-
- CGnuTransfer::~CGnuTransfer()
- {
- IsTransfering = 0;
-
- if(m_threadHandle)
- {
- switch (::WaitForSingleObject (m_threadHandle, 10000))
- {
- case WAIT_OBJECT_0: // Normal, the thread exited properly
- break;
- case WAIT_TIMEOUT: // Thread didn't exit, kill it
- ::TerminateThread (m_threadHandle, 0);
- break;
- case WAIT_FAILED: // Thread probably exited before we got to the waitforsingleobject
- break;
- default: // err
- ::TerminateThread (m_threadHandle, 0);
- break;
- }
- }
- }
-
-
- // Do not edit the following lines, which are needed by ClassWizard.
- #if 0
- BEGIN_MESSAGE_MAP(CGnuTransfer, CAsyncSocket)
- //{{AFX_MSG_MAP(CGnuTransfer)
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- #endif // 0
-
- /////////////////////////////////////////////////////////////////////////////
- // CGnuTransfer member functions
-
- void CGnuTransfer::OnConnect(int nErrorCode)
- {
- if(nErrorCode == 0)
- {
- if(Type == 'D')
- {
- Status = "Remotely queued";
-
- CString GetCommand = "GET /get/";
- GetCommand += DWrdtoStr(FileInfo.Index);
- GetCommand += "/";
- GetCommand += FileInfo.FileName;
- GetCommand += " HTTP/1.0\r\nConnection: Keep-Alive\r\nRange: bytes=";
- GetCommand += DWrdtoStr(BytesCompleted);
- GetCommand += "-\r\n\r\n";
-
- Send(GetCommand, GetCommand.GetLength());
-
- GnuComm->BytesOut += GetCommand.GetLength ();
- }
-
- // Incoming push
- if(Type == 'U')
- {
- byte *ClientID = (byte *) &FileInfo.Guid;
-
- std::basic_string<char> GiveMsg("GIV ");
- GiveMsg += DWrdtoStr(FileInfo.Index) + ": ";
-
- TCHAR buff[33];
- sprintf(buff, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- ClientID[0], ClientID[1], ClientID[2], ClientID[3], ClientID[4],
- ClientID[5], ClientID[6], ClientID[7], ClientID[8], ClientID[9],
- ClientID[10], ClientID[11], ClientID[12], ClientID[13], ClientID[14],
- ClientID[15]);
-
- GiveMsg.append(buff);
-
- std::vector<SharedFile>::iterator it;
-
- int loop;
- for(loop = 0, it = GnuComm->Doc->SharedFiles.begin();
- it != GnuComm->Doc->SharedFiles.end();
- loop++, it++)
- {
- if(loop == FileInfo.Index)
- FileInfo.FileName = (*it).FileName;
- }
-
- if(FileInfo.FileName == "")
- return;
-
- GiveMsg += "/" + FileInfo.FileName + "\n\n";
-
- Send(GiveMsg.c_str(), GiveMsg.length());
- }
- }
- else
- Status = "Connection Refused";
-
-
- CAsyncSocket::OnConnect(nErrorCode);
- }
-
- void CGnuTransfer::OnReceive(int nErrorCode)
- {
- DWORD dwSize = 0;
- int iReceiveLength = 0;
- int length = 0;
-
- if (!IOCtl (FIONREAD, &dwSize))
- {
- // If we can't tell how many bytes are waiting on the socket, something's gone wrong.
- Status = "Bad Socket";
- Close ();
-
- return;
- }
-
- byte *pBuff = new byte[dwSize+1];
-
- iReceiveLength = Receive(pBuff, dwSize);
-
- switch (iReceiveLength)
- {
- case 0:
- // Connection has been closed, shutdown
-
- WantToDisconnect();
- // m_CanRelease = true;
- // CleanUp();
- break;
-
- case SOCKET_ERROR:
- OutputDebugString("CGnuSock::OnReceive - SOCKET_ERROR\n");
-
- // received an error, see what it is and handle.
- switch(GetLastError())
- {
- // fatal errors, need to disconnect
- default:
- case WSAECONNRESET: // The virtual circuit was reset by the remote side.
- case WSAENOTCONN: // The socket is not connected.
- case WSAENOTSOCK: // The descriptor is not a socket.
- case WSAESHUTDOWN: // The socket has been shut down
- case WSAECONNABORTED: // The virtual circuit was aborted due to timeout or other failure.
- case WSAEWOULDBLOCK: //The socket is marked as nonblocking and the Receive operation would block. Shouldn't happen.
- case WSAENETDOWN: // detected that the network subsystem failed
-
- WantToDisconnect();
- // m_CanRelease = true;
- // CleanUp();
-
- break;
-
- // non fatal errors, just handle individualy
- case WSAEMSGSIZE: // The datagram was too large to fit into the specified buffer and was truncated.
- // Shouldn't happen because buffer is veriable sized
- WantToDisconnect();
- // m_CanRelease = true;
- // CleanUp();
-
- break;
- }
- delete [] pBuff;
- return;
- break;
- default: // just normal data
- m_timeLastHeardAT = CTime::GetCurrentTime();
- length += iReceiveLength;
- }
-
-
- m_dwBytesIn += length;
- GnuComm->BytesIn += length;
-
- pBuff[length] = 0;
-
- CString FileHeader = (char *) pBuff;
-
- // If this is an upload, get out of here
- if(FileHeader.Find( "GET /get/") != -1)
- ((CViewTransfer *) ((CGnucleusApp *) AfxGetApp())->TransferFrame->GetActiveView())->NewUpload(FileHeader, NULL);
-
- else if(IsTransfering == 0)
- {
- // Be ready for response in the format of "HTTP/1.1 <Response-code>".
- // Anything that doesn't start with HTTP should still be dropped
- if(FileHeader.Find("HTTP 200 OK\r\nServer: ") != -1)
- {
- FileHeader.MakeLower();
-
- // Extract client sending and file size from the header
- int front = FileHeader.Find("server: ") + 8;
- int back = FileHeader.Find("\r\n", front);
- ClientName = FileHeader.Mid(front, back - front);
-
- if(BytesCompleted)
- {
- front = FileHeader.Find("bytes=", back) + 6;
- front = FileHeader.Find("-", front) + 1;
- back = FileHeader.Find("/", front);
- }
- else
- {
- front = FileHeader.Find("content-length:") + 15;
- back = FileHeader.Find("\r\n\r\n", front);
-
- FileSize = atol( FileHeader.Mid(front, back - front));
- }
-
- IsTransfering = 1;
- Status = "Downloading";
-
- // Create the file and start initial d/l
- if(BytesCompleted)
- {
- if(File.Open(GnuComm->Doc->m_DownloadDir + "\\" + (FileInfo.LocalFileName.IsEmpty() ? FileInfo.FileName : FileInfo.LocalFileName), CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite | CFile::shareDenyNone))
- IsFileOpen = 1;
-
- File.SeekToEnd();
- }
- else
- {
- if (FileInfo.Size == 0)
- {
- FileInfo.Size = FileSize;
- }
- if(FileSize != FileInfo.Size)
- {
- Close();
- Status = "File Invalid";
-
- delete [] pBuff;
- return;
- }
-
- if(File.Open(GnuComm->Doc->m_DownloadDir + "\\" + (FileInfo.LocalFileName.IsEmpty() ? FileInfo.FileName : FileInfo.LocalFileName), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone))
- IsFileOpen = 1;
- }
-
- int begin = FileHeader.Find("\r\n\r\n", front) + 4;
- File.Write(&pBuff[begin], length - begin);
- BytesCompleted += length - begin;
- }
- else
- {
- int httpError = 0;
-
- ::sscanf (FileHeader, "HTTP %d %*s", &httpError);
-
- if (httpError != 0)
- {
- CString err;
-
- Status.Format ("HTTP %d Error", httpError);
- }
- else
- {
- Status = "Bad Header";
- }
- Close();
- }
- }
- else
- {
- if(BytesCompleted < FileSize)
- {
- File.Write(pBuff, length);
- BytesCompleted += length;
- }
- else
- {
- IsTransfering = 0;
- Close();
-
- if(IsFileOpen)
- File.Close();
-
- IsFileOpen = false;
- }
-
- if(BytesCompleted == FileSize)
- Status = "Completed";
- }
-
- m_timeLastHeardAT = CTime::GetCurrentTime();
-
- delete [] pBuff;
-
- CAsyncSocket::OnReceive(nErrorCode);
- }
-
- void CGnuTransfer::OnClose(int nErrorCode)
- {
- IsTransfering = 0;
-
- if(IsFileOpen)
- File.Close();
- IsFileOpen = false;
-
- // clean out the buffer
- char * pBuf = new char[101];
- int iRet = 0;
- while( (iRet = Receive(pBuf, 100)) != 0 && iRet != SOCKET_ERROR );
- delete [] pBuf;
-
- if(BytesCompleted == FileSize)
- Status = "Completed";
- else
- Status = "Remotely Canceled";
-
- CAsyncSocket::OnClose(nErrorCode);
- }
-
- void CGnuTransfer::Pause()
- {
- IsTransfering = 0;
- WantToDisconnect();
- Close();
-
- if(IsFileOpen)
- File.Close();
- IsFileOpen = false;
-
- Status = "Paused";
- }
-
- void CGnuTransfer::Resume()
- {
- Status = "Active";
- }
-
- void CGnuTransfer::StartSending()
- {
- // Send the header
- std::basic_string<char> Header("HTTP 200 OK\r\nServer: Gnutella\r\nContent-type:application/binary\r\n");
-
- if(BytesCompleted)
- Header += "Content-length: bytes=" + DWrdtoStr(BytesCompleted) + "-" + DWrdtoStr(FileSize - 1) + "/" + DWrdtoStr(FileSize) + "-\r\n\r\n";
- else
- Header += "Content-length:" + DWrdtoStr(FileSize) + "\r\n\r\n";
-
- // Send the header
- if(Header.length() != Send( Header.c_str(), Header.length()))
- Close();
-
- if( !File.Open(FileInfo.FileName, CFile::modeRead | CFile::shareDenyNone) )
- return;
-
- IsTransfering = 1;
- IsFileOpen = 1;
- Status = "Uploading";
-
- File.Seek(BytesCompleted, CFile::begin);
-
- m_threadHandle = (HANDLE) ::_beginthread (SendFile, 0, this);
- }
-
- void CGnuTransfer::WantToDisconnect()
- {
- // Let spock know that he is going to die, so it might as well
- // speed up the process.
-
- if(m_hSocket != INVALID_SOCKET)
- {
- AsyncSelect(FD_CLOSE);
- ShutDown(sends);
- }
-
- IsTransfering = false;
- }
-
- void CGnuTransfer::SendFile(void *Link)
- {
- CGnuTransfer *GnuTrans = (CGnuTransfer *) Link;
- CGnucleusDoc *Doc = GnuTrans->GnuComm->Doc;
-
-
- int AllocBytes = 32000;
-
- /*
- if(Doc->m_LimitUp)
- GnuTrans->AllocBw = Doc->m_LimitUp * 1024 / (1 + Doc->ActiveUploads);
- else
- GnuTrans->AllocBw = -1;
-
- // Bandwidth management for uploads
- if(GnuTrans->AllocBw != -1)
- {
- if(GnuTrans->AllocBw > 32000)
- GnuTrans->AllocBw -= 32000;
- else
- {
- AllocBytes = GnuTrans->AllocBw;
- GnuTrans->AllocBw = 0;
- }
- }
- */
-
- if(Doc->m_LimitUp)
- GnuTrans->m_dwByteAllottmentOut = Doc->m_LimitUp * 1024 / 2; // preliminary allotment
- else if(Doc->m_LimitTotal)
- GnuTrans->m_dwByteAllottmentOut = Doc->m_LimitTotal * 1024 / 3;// preliminary allotment
-
- if(GnuTrans->m_dwByteAllottmentOut)
- {
- if(GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut < 32000) // if there isn't 32k available, take what is.
- {
- AllocBytes = GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut;
- }
- }
-
-
- BYTE *buffer = new BYTE[AllocBytes];
-
- int bytesRead = 0;
- if(GnuTrans->IsFileOpen)
- bytesRead = GnuTrans->File.Read(buffer, AllocBytes);
-
- while(GnuTrans->IsTransfering &&
- GnuTrans->BytesCompleted != GnuTrans->FileSize &&
- bytesRead > 0)
- {
- int bytesSent = 0;
-
- if(GnuTrans->IsTransfering)
- {
- bytesSent = GnuTrans->Send(buffer + bytesSent, bytesRead - bytesSent);
- GnuTrans->GnuComm->BytesOut += bytesSent;
- GnuTrans->m_dwBytesOut += bytesSent;
- }
- while(GnuTrans->IsTransfering && bytesSent < bytesRead)
- {
- if(GetLastError() == WSAEWOULDBLOCK)
- bytesSent += 1;
- else if(GetLastError())
- {
- if(GnuTrans->IsTransfering)
- GnuTrans->Status = "Error sending";
-
- delete [] buffer;
- return;
- }
-
- if(GnuTrans->IsTransfering)
- {
- bytesSent += GnuTrans->Send(buffer + bytesSent, bytesRead - bytesSent);
- GnuTrans->GnuComm->BytesOut += bytesSent;
- GnuTrans->m_dwBytesOut += bytesSent;
- }
- }
-
- if(GnuTrans->IsTransfering)
- {
- GnuTrans->BytesCompleted += bytesSent;
- GnuTrans->m_dwTotalBytesOut += bytesSent;
-
- if(GnuTrans->BytesCompleted == GnuTrans->FileSize)
- {
- GnuTrans->Status = "Completed";
- GnuTrans->IsTransfering = 0;
- }
- else
- {
- delete [] buffer;
-
- AllocBytes = 32000;
-
- // Bandwidth management for uploads
- /*
- if(GnuTrans->AllocBw != -1)
- {
- while(GnuTrans->IsTransfering && !GnuTrans->AllocBw);
-
- if(GnuTrans->AllocBw > 32000)
- GnuTrans->AllocBw -= 32000;
- else
- {
- AllocBytes = GnuTrans->AllocBw;
- GnuTrans->AllocBw = 0;
- }
- }
- */
- if(GnuTrans->m_dwByteAllottmentOut)
- {
- while (GnuTrans->IsTransfering
- && (GnuTrans->m_dwBytesOut >= GnuTrans->m_dwByteAllottmentOut)
- && GnuTrans->m_dwByteAllottmentOut)
- Sleep(50); // wait half a second
-
- if(GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut < 32000) // if there isn't 32k available, take what is.
- {
- AllocBytes = GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut;
- }
- }
-
- if(!AllocBytes)
- AllocBytes = 32000;
-
- buffer = new BYTE[AllocBytes];
-
- if(GnuTrans->IsFileOpen)
- bytesRead = GnuTrans->File.Read(buffer, AllocBytes);
- }
- }
- }
-
- delete [] buffer;
- }
-
-
-